
package XMLStuff;

import JET.Centers.ControlCenter;
import JET.Centers.Scripts.MyScript;
import JET.Constants;
import JET.Frames.DataModels.DataModelsFactory;
import JET.Scene;
import JET.SceneHelper;
import JET.geom.Vec2d;
import JET.physics.Mass;
import JET.physics.RSpring;
import JET.physics.Spring;
import JET.physics.Wall;
import JET.physics.bodies.Body;
import JET.physics.bodies.RigidBody;
import JET.physics.bodies.SpringBody;
import JET.physics.engine.JETContext;
import JET.physics.engine.JETContextHelper;
import JET.physics.steering.Torque;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 *
 * @author raos
 */
public class XMLHelper2 implements XMLConstants, Constants {

    public static final ControlCenter CC = ControlCenter.getInstance();


    ////////////////////////////////////////////////////////////////////////////
    //                    E L M E N T S   B U I L D E R S                     //
    ////////////////////////////////////////////////////////////////////////////

//    public static Element getElementByName(String name, Element src) {
//        NodeList nl = src.getElementsByTagName(name);
//        return (Element) nl.item(0);
//    }

    /**
     * Creates xml node describing some vector value like velocity r force.
     * @param doc Document that is used to create new vector element node.
     * @param name Name of vector value
     * @param vec Vector value itself
     * @return new xml vector element node.
     */
    public static Element buildVec2dElement(Document doc, String name, Vec2d vec) {
        Element e = doc.createElement(name);
        e.setAttribute("x", ""+vec.getX());
        e.setAttribute("y", ""+vec.getY());
        return e;
    }
//
//    /**
//     * Parses xml vector element and creates new instance of Vec2d object.
//     * @param e xml vector element node.
//     * @return New Vec2d object.
//     */
//    public static Vec2d parseVec2dElement(Element e) {
//        Vec2d v = new Vec2d();
//        v.setX(Double.parseDouble(e.getAttribute("x")));
//        v.setY(Double.parseDouble(e.getAttribute("y")));
//        return v;
//    }

    /**
     * Creates xml mass node element of specified Mass object.
     * @param doc Docuemnt that is ised to create new mass element node.
     * @param mass Mass object to be parsed
     * @return new xml mass element node.
     */
    public static Element buildMassElement(Document doc, Mass m) {
        Element e = doc.createElement(ELEMENT_NAME_MASS);
        e.setAttribute("name", m.getName());
        e.setAttribute("mass", ""+m.getMass());
        e.setAttribute("moveable", ""+m.isMoveable());
        e.appendChild(buildVec2dElement(doc, "pos", m.getPosition()));
        e.appendChild(buildVec2dElement(doc, "vel", m.getVelocity()));
        e.appendChild(buildVec2dElement(doc, "force", m.getForce()));
        return e;
    }

    public static Element buildSpringElement(Document doc, Spring s, SpringBody sb) {
        Element e = doc.createElement(ELEMENT_NAME_SPRING);
        e.setAttribute("name", s.getName());
        e.setAttribute("ks", ""+s.getKs());
        e.setAttribute("kd", ""+s.getKd());
        e.setAttribute("kt", ""+s.getKt());
        e.setAttribute("startlength",""+s.getStartLength());
        e.setAttribute("tearable", ""+s.isTearable());
        e.setAttribute("alive", ""+s.isAlive());
        e.setAttribute("mass1", ""+sb.getMassList().indexOf(s.getM1()));
        e.setAttribute("mass2", ""+sb.getMassList().indexOf(s.getM2()));
        e.setAttribute("m1Type", "Spring");
        e.setAttribute("m2Type", "Spring");
        e.setAttribute("type", ""+s.getType());
        return e;
    }

    public static Element buildSpringElement(Document doc, Spring s, JETContext context) {
        Element e = doc.createElement(ELEMENT_NAME_SPRING);
        e.setAttribute("name", s.getName());
        e.setAttribute("ks", ""+s.getKs());
        e.setAttribute("kd", ""+s.getKd());
        e.setAttribute("kt", ""+s.getKt());
        e.setAttribute("startlength",""+s.getStartLength());
        e.setAttribute("tearable", ""+s.isTearable());
        e.setAttribute("alive", ""+s.isAlive());

        int bodyIndex = -1,
            massIndex = -1;
        String massType = "none"; // [Spring | Rigid]
        
        Body b = JETContextHelper.getBodyThatOwnsMass(context, s.getM1());
        if (b instanceof SpringBody) {
            bodyIndex = context.getSpringBodyList().indexOf(b);
            massIndex = ((SpringBody)b).getMassList().indexOf(s.getM1());
            massType = "Spring";
        }
        else if(b instanceof RigidBody) {
            bodyIndex = context.getRigidBodyList().indexOf(b);
            massIndex = ((RigidBody)b).extraDynMassList.indexOf(s.getM1());
            massType = "Rigid";
        }

        e.setAttribute("mass1", ""+massIndex);
        e.setAttribute("body1", ""+bodyIndex);
        e.setAttribute("m1Type", ""+massType);

        b = JETContextHelper.getBodyThatOwnsMass(context, s.getM2());
        if (b instanceof SpringBody) {
            bodyIndex = context.getSpringBodyList().indexOf(b);
            massIndex = ((SpringBody)b).getMassList().indexOf(s.getM2());
            massType = "Spring";
        }
        else if(b instanceof RigidBody) {
            bodyIndex = context.getRigidBodyList().indexOf(b);
            massIndex = ((RigidBody)b).extraDynMassList.indexOf(s.getM2());
            massType = "Rigid";
        }

        e.setAttribute("mass2", ""+massIndex);
        e.setAttribute("body2", ""+bodyIndex);
        e.setAttribute("m2Type", ""+massType);

        e.setAttribute("type", ""+s.getType());
        return e;
    }

    public static Element buildRSpringElement(Document doc, RSpring s, SpringBody sb) {
        Element e = doc.createElement(ELEMENT_NAME_RSPRING);
        e.setAttribute("name", s.getName());
        e.setAttribute("ks", ""+s.getKs());
        e.setAttribute("kd", ""+s.getKd());
        e.setAttribute("startangle", ""+s.getStartAngle());
        e.setAttribute("alive", ""+s.isAlive());
        e.setAttribute("mass1", ""+sb.getMassList().indexOf(s.getM1()));
        e.setAttribute("mass2", ""+sb.getMassList().indexOf(s.getM2()));
        e.setAttribute("mass3", ""+sb.getMassList().indexOf(s.getM3()));
        return e;
    }

    public static Element buildRSpringElement(Document doc, RSpring s, JETContext engine) {
        Element e = doc.createElement(ELEMENT_NAME_RSPRING);
        e.setAttribute("name", s.getName());
        e.setAttribute("ks", ""+s.getKs());
        e.setAttribute("kd", ""+s.getKd());
        e.setAttribute("startangle", ""+s.getStartAngle());
        e.setAttribute("alive", ""+s.isAlive());
        SpringBody sb1 = JETContextHelper.getSpringBodyThatOwnMass(engine, s.getM1());
        SpringBody sb2 = JETContextHelper.getSpringBodyThatOwnMass(engine, s.getM2());
        SpringBody sb3 = JETContextHelper.getSpringBodyThatOwnMass(engine, s.getM3());
        e.setAttribute("mass1", ""+sb1.getMassList().indexOf(s.getM1()));
        e.setAttribute("mass2", ""+sb2.getMassList().indexOf(s.getM2()));
        e.setAttribute("mass3", ""+sb3.getMassList().indexOf(s.getM3()));
        e.setAttribute("body1", ""+engine.getSpringBodyList().indexOf(sb1));
        e.setAttribute("body2", ""+engine.getSpringBodyList().indexOf(sb2));
        e.setAttribute("body3", ""+engine.getSpringBodyList().indexOf(sb3));
        return e;
    }

    public static Element buildSMSpringElement(Document doc, Spring s, SpringBody sb) {
        Element e = doc.createElement(ELEMENT_NAME_SMSPRING);
        e.setAttribute("name", s.getName());
        e.setAttribute("ks", ""+s.getKs());
        e.setAttribute("kd", ""+s.getKd());
        e.setAttribute("startlength", ""+s.getStartLength());
        e.setAttribute("sbmass", ""+sb.getMassList().indexOf(s.getM1()));
        e.setAttribute("rbmass", ""+sb.rb.dynVecList.indexOf(s.getM2()));
        return e;
    }

    public static Element buildWallElement(Document doc, Wall w, SpringBody sb) {
        Element e = doc.createElement(ELEMENT_NAME_WALL);
        e.setAttribute("mass1", ""+sb.getMassList().indexOf(w.m1));
        e.setAttribute("mass2", ""+sb.getMassList().indexOf(w.m2));
        return e;
    }
    public static Element buildWallElement(Document doc, Wall w, RigidBody rb) {
        Element e = doc.createElement(ELEMENT_NAME_WALL);
        e.setAttribute("mass1", ""+rb.dynVecList.indexOf(w.m1));
        e.setAttribute("mass2", ""+rb.dynVecList.indexOf(w.m2));
        return e;
    }

    public static Element buildRigidBodyVertexElement(Document doc, ArrayList<Vec2d> baseList, ArrayList<Mass> dynList, int index) {
        Element e = doc.createElement("rbvertex");
        Vec2d v = baseList.get(index);
        Mass m = dynList.get(index);
        e.setAttribute("baseX", ""+v.x);
        e.setAttribute("baseY", ""+v.y);
        e.setAttribute("dynX", ""+m.getPosition().x);
        e.setAttribute("dynY", ""+m.getPosition().y);
        e.setAttribute("mass", ""+m.mass);
        return e;
    }
    public static Element buildRigidBodyElement(Document doc, RigidBody rb) {
        Element e = doc.createElement(ELEMENT_NAME_RIGIDBODY);

        e.setAttribute("name", rb.getName());
        e.setAttribute("material", ""+rb.getMaterial());
        e.setAttribute("angle", ""+rb.angle);
        e.setAttribute("mass", ""+rb.mass);
        e.setAttribute("mi", ""+rb.mI);
        e.setAttribute("omega", ""+rb.omega);
        e.setAttribute("torque", ""+rb.torque);
        e.setAttribute("posX", ""+rb.position.x);
        e.setAttribute("posY", ""+rb.position.y);
        
        e.setAttribute("velX", ""+rb.velocity.x);
        e.setAttribute("velY", ""+rb.velocity.y);
        
        e.setAttribute("posX", ""+rb.position.x);
        e.setAttribute("posY", ""+rb.position.y);
        
        e.setAttribute("forceX", ""+rb.force.x);
        e.setAttribute("forceY", ""+rb.force.y);

        Element eRBVs = doc.createElement("rbvertices");
        for(int i=0; i<rb.baseVecList.size(); i++)
            eRBVs.appendChild(buildRigidBodyVertexElement(doc, rb.baseVecList, rb.dynVecList, i));
        e.appendChild(eRBVs);

//        // Base shape vectors
//        Element eBSV = doc.createElement(ELEMENT_NAME_BASESHAPEVECTORS);
//        for(Vec2d v : rb.baseVecList)
//            eBSV.appendChild(buildVec2dElement(doc, ELEMENT_NAME_BASESHAPEVECTOR, v));
//        e.appendChild(eBSV);
//
//        // Dynamic masses
//        Element eDVL = doc.createElement(ELEMENT_NAME_MASSES);
//        for(Mass m : rb.dynVecList)
//            eDVL.appendChild(buildMassElement(doc, m));
//        e.appendChild(eDVL);

        // Walls

        Element eExtraVertices = doc.createElement("rbextravertices");
        for(int i=0; i<rb.extraBaseVecList.size(); i++)
            eExtraVertices.appendChild(buildRigidBodyVertexElement(doc, rb.extraBaseVecList, rb.extraDynMassList, i));
        e.appendChild(eExtraVertices);

        Element eWalls = doc.createElement(ELEMENT_NAME_WALLS);
        Collection<Wall> wallCol = rb.border.getWallListSync();
        synchronized(wallCol) {
            for(Wall w : wallCol)
                eWalls.appendChild(buildWallElement(doc, w, rb));
        }
        e.appendChild(eWalls);

//        // Extra masses base vectors
//        Element eExtraBaseVecs = doc.createElement(ELEMENT_NAME_EXTRABASEVECS);
//            for(Vec2d v : rb.extraBaseVecList)
//                eExtraBaseVecs.appendChild(buildVec2dElement(doc, ELEMENT_NAME_EXTRABASEVEC, v));
//        e.appendChild(eExtraBaseVecs);
//
//        // Extra masses
//        Element eExtraMasses = doc.createElement(ELEMENT_NAME_EXTRAMASSES);
//            for(Mass m : rb.extraDynMassList)
//                eExtraMasses.appendChild(buildMassElement(doc, m));
//        e.appendChild(eExtraMasses);

        return e;
    }

    public static Element buildSpringBodyElement(Document doc, SpringBody sb) {
        Element eSB = doc.createElement(ELEMENT_NAME_SPRINGBODY);

        eSB.setAttribute("name", sb.getName());
        eSB.setAttribute("material", ""+sb.getMaterial());
        eSB.setAttribute("matchshape", ""+sb.isMatchShape());
        if (sb.smMassIndices!=null)
            eSB.setAttribute("smmi", buildAttr_SBSMMIndices(sb));
        
        Element eMasses = doc.createElement(ELEMENT_NAME_MASSES);
        Collection<Mass> massCol = sb.getMassListSync();
        synchronized(massCol) {
            for(Mass m : massCol)
                eMasses.appendChild(buildMassElement(doc, m));
        }
        eSB.appendChild(eMasses);

        Element eSprings = doc.createElement(ELEMENT_NAME_SPRINGS);
        Collection<Spring> springCol = sb.getSpringListSync();
        synchronized(springCol) {
            for(Spring s : springCol)
                eSprings.appendChild(buildSpringElement(doc, s, sb));
        }
        eSB.appendChild(eSprings);

        Element eRSprings = doc.createElement(ELEMENT_NAME_RSPRINGS);
        Collection<RSpring> rSpringCol = sb.getRSpringListSync();
        synchronized(rSpringCol) {
            for(RSpring s : rSpringCol)
                eRSprings.appendChild(buildRSpringElement(doc, s, sb));
        }
        eSB.appendChild(eRSprings);

        Element eWalls = doc.createElement(ELEMENT_NAME_WALLS);
        Collection<Wall> wallCol = sb.getBorder().getWallListSync();
        synchronized(wallCol) {
            for(Wall w : wallCol)
                eWalls.appendChild(buildWallElement(doc, w, sb));
        }
        eSB.appendChild(eWalls);

        if (sb.rb!=null)
            eSB.appendChild(buildRigidBodyElement(doc, sb.rb));

        if (sb.smSprings!=null) {
            Element eSMSprings = doc.createElement(ELEMENT_NAME_SMSPRINGS);
            eSMSprings.setAttribute("count", ""+sb.smSprings.length);
            for(Spring s : sb.smSprings)
                eSMSprings.appendChild(buildSMSpringElement(doc, s, sb));
            eSB.appendChild(eSMSprings);
        }

        return eSB;
    }

    public static Element buildScriptElement(Document doc, MyScript s) {
        Element e = doc.createElement(ELEMENT_NAME_SCRIPT);
        e.setAttribute("name", s.getName());
        e.appendChild(doc.createCDATASection(s.getScript()));
        return e;
    }

    public static Element buildMaterialPairElement(Document doc, Scene s, int r, int c) {
        Element e = doc.createElement(ELEMENT_NAME_MATERIALPAIR);
        e.setAttribute("r", ""+r);
        e.setAttribute("c", ""+c);
        e.setAttribute("elasticity", ""+s.getJETContext().materials[r][c].elasticity);
        e.setAttribute("friction", ""+s.getJETContext().materials[r][c].friction);
        e.setAttribute("collide", ""+s.getJETContext().materials[r][c].collide);
        return e;
    }

    public static Element buildTorqueElement(Document doc, Scene s, Torque t) {
        Element e = doc.createElement(ELEMENT_NAME_TORQUE);

        e.setAttribute("name", t.getName());
        e.setAttribute("active", ""+t.isActive());
        e.setAttribute("strength", ""+t.getValue());
        e.setAttribute("center-type", "mass");

        SpringBody sb = SceneHelper.getSpringBodyThatOwnMass(s, t.getMass());
        int sbIndex = s.getJETContext().getSpringBodyList().indexOf(sb);
        int massIndex = sb.getMassList().indexOf(t.getMass());

        e.setAttribute("center-mass", ""+massIndex);
        e.setAttribute("center-body", ""+sbIndex);

        HashMap<Integer, String> map = buildAttr_TorqueMassIndices(t, s);
        for(Integer key : map.keySet()) {
            Element eMasslist = doc.createElement(ELEMENT_NAME_MASSLIST);
            eMasslist.setAttribute("body", ""+key);
            eMasslist.setAttribute("masses", ""+map.get(key));
            e.appendChild(eMasslist);
        }

        return e;
    }

    public static Element buildSceneElement(Document doc, Scene s) {
        Element e = doc.createElement(ELEMENT_NAME_SCENE);

        e.setAttribute("name", s.getName());
        e.setAttribute("path", CC.getSceneFilterPath(s));

        Element eSimulation = doc.createElement(ELEMENT_NAME_SIMULATION);
        eSimulation.setAttribute("apply-gravity", ""+s.getJETContext().isApplyGravity());
        eSimulation.setAttribute("gravity", ""+s.getJETContext().gravity);
        eSimulation.setAttribute("apply-damping", ""+s.getJETContext().isApplyDamping());
        eSimulation.setAttribute("damping", ""+s.getJETContext().damping);
        eSimulation.setAttribute("timestep", ""+s.getJETContext().getDTime());
        eSimulation.setAttribute("width", ""+s.getJETContext().getMainBound().getWidth());
        eSimulation.setAttribute("height", ""+s.getJETContext().getMainBound().getHeight());
        e.appendChild(eSimulation);

        Element eSBs = doc.createElement(ELEMENT_NAME_SPRINGBODIES);
        Collection<SpringBody> sbCol = s.getJETContext().getSpringBodyListSync();
        synchronized(sbCol) {
            for(SpringBody sb : sbCol)
                eSBs.appendChild(buildSpringBodyElement(doc, sb));
        }
        e.appendChild(eSBs);

        Element eRBs = doc.createElement(ELEMENT_NAME_RIGIDBODIES);
        Collection<RigidBody> rbCol = s.getJETContext().getRigidBodyListSync();
        synchronized(rbCol) {
            for(RigidBody rb : rbCol)
                eRBs.appendChild(buildRigidBodyElement(doc, rb));
        }
        e.appendChild(eRBs);

        Element eESprings = doc.createElement(ELEMENT_NAME_SPRINGS);
        Collection<Spring> springCol = s.getJETContext().getSpringListSync();
        synchronized(springCol) {
            for(Spring spring : springCol)
                eESprings.appendChild(buildSpringElement(doc, spring, s.getJETContext()));
        }
        e.appendChild(eESprings);

        Element eERSprings = doc.createElement(ELEMENT_NAME_RSPRINGS);
        Collection<RSpring> rSpringCol = s.getJETContext().getRSpringListSync();
        synchronized(springCol) {
            for(RSpring spring : rSpringCol)
                eERSprings.appendChild(buildRSpringElement(doc, spring, s.getJETContext()));
        }
        e.appendChild(eERSprings);

        Element eTorques = doc.createElement(ELEMENT_NAME_TORQUES);
        Collection<Torque> torqueCol = s.getJETContext().getTorqueListSync();
        synchronized(torqueCol) {
            for(Torque t : torqueCol)
                eTorques.appendChild(buildTorqueElement(doc, s, t));
        }
        e.appendChild(eTorques);

        Element eScripts = doc.createElement(ELEMENT_NAME_SCRIPTS);
        Collection<MyScript> scCol = s.SC.getScriptsSync();
        synchronized(scCol) {
            for(MyScript ms : scCol)
                eScripts.appendChild(buildScriptElement(doc, ms));
        }
        e.appendChild(eScripts);

        Element eMaterials = doc.createElement(ELEMENT_NAME_MATERIALS);
        String names = "";
        int count = s.getJETContext().materialsCount;
        for(int i=0; i<count; i++)
            names += s.getJETContext().materialNames[i] + (i<count-1?",":"");
        eMaterials.setAttribute("materials", names);
        
        for(int r=0; r<count; r++)
            for(int c=0; c<count; c++)
                eMaterials.appendChild(buildMaterialPairElement(doc, s, r, c));
        e.appendChild(eMaterials);
        
        return e;
    }

    public static Element buildProjectElement() throws ParserConfigurationException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.newDocument();

        Element e = doc.createElement(ELEMENT_NAME_PROJECT);

        Element eScenes = doc.createElement(ELEMENT_NAME_SCENES);
        Collection<Scene> sceneCol = CC.getSceneListSync();
        synchronized(sceneCol) {
            for(Scene s : sceneCol)
                eScenes.appendChild(buildSceneElement(doc, s));
        }
        e.appendChild(eScenes);
        
        return e;
    }

    ////////////////////////////////////////////////////////////////////////////
    //              A T T R I B U T E S   B U I L D E R S                     //
    ////////////////////////////////////////////////////////////////////////////

    /**
     * Builds spring body shape matching masses indices.
     * @param sb Spring body object that attribute we want to build.
     * @return String representing shape matching masses indices.
     *         Indexes are separated by commas.
     */
    public static String buildAttr_SBSMMIndices(SpringBody sb) {
        String s = "";
        for(int i=0; i<sb.smMassIndices.length; i++)
            s += sb.smMassIndices[i] + (i<sb.smMassIndices.length-1 ? "," : "");

        return s;
    }

    /**
     * Builds torque masses indices.
     * @param t Torque object that attribute we want to build.
     * @return String representing masses indices.
     *         Indexes are separated by commas.
     */
    public static HashMap<Integer, String> buildAttr_TorqueMassIndices(Torque t, Scene s) {
        HashMap<Integer, String> map = new HashMap<Integer,String>();
        Collection<Mass> massCol = t.getMassListSync();
        synchronized(massCol) {
            for(Mass m : massCol) {
                SpringBody sb = SceneHelper.getSpringBodyThatOwnMass(s, m);
                int sbIndex = s.getJETContext().getSpringBodyList().indexOf(sb);
                String masses = map.get(new Integer(sbIndex));
                if (masses==null)
                {
                    masses = ""+ sb.getMassList().indexOf(m);
                    map.put(new Integer(sbIndex), masses);
                }
                else
                {
                    masses += ", "+sb.getMassList().indexOf(m);
                    map.put(new Integer(sbIndex), masses);
                }
            }
        }

        return map;
    }

    ////////////////////////////////////////////////////////////////////////////
    //                J E T   O B J E C T S   B U I L D E R S                 //
    ////////////////////////////////////////////////////////////////////////////

    public static Vec2d buildVec2d(Attributes atts) {
        return new Vec2d(Double.parseDouble(atts.getValue("x")), Double.parseDouble(atts.getValue("y")));
    }

    public static Mass buildMass(Attributes atts) {
        Mass m = new Mass(0,0,1);
        m.setName(atts.getValue("name"));
        m.setMass(Double.parseDouble(atts.getValue("mass")));
        m.setMoveable(Boolean.parseBoolean(atts.getValue("moveable")));
        return m;
    }

    public static Spring buildSpring(Attributes atts, JETContext c, SpringBody sb, RigidBody rb) {
        Spring s = null;
        Mass m1=null, m2=null;

        String type = atts.getValue("type");
        if ("SpringBody".equals(type))
        {
            s = new Spring(Spring.Type.SpringBody);
            s.setM1(sb.getMassList().get(Integer.parseInt(atts.getValue("mass1"))));
            s.setM2(sb.getMassList().get(Integer.parseInt(atts.getValue("mass2"))));
        }
        else if ("Engine".equals(type))
        {
            s = new Spring(Spring.Type.Engine);
           
            String m1Type = atts.getValue("m1Type");
            if       ("Spring".equals(m1Type))   m1 = c.getSpringBodyList().get(Integer.parseInt(atts.getValue("body1"))).getMassList().get(Integer.parseInt(atts.getValue("mass1")));
            else if  ("Rigid".equals(m1Type))    m1 = c.getRigidBodyList().get(Integer.parseInt(atts.getValue("body1"))).extraDynMassList.get(Integer.parseInt(atts.getValue("mass1")));
            s.setM1(m1);
           
            String m2Type = atts.getValue("m2Type");
            if       ("Spring".equals(m2Type))   m2 = c.getSpringBodyList().get(Integer.parseInt(atts.getValue("body2"))).getMassList().get(Integer.parseInt(atts.getValue("mass2")));
            else if  ("Rigid".equals(m2Type))    m2 = c.getRigidBodyList().get(Integer.parseInt(atts.getValue("body2"))).extraDynMassList.get(Integer.parseInt(atts.getValue("mass2")));
            s.setM2(m2);
        }
//        else {
//            if (sb!=null) {
//                s = new Spring(Spring.Type.SpringBody);
//                s.setM1(sb.getMassList().get(Integer.parseInt(atts.getValue("mass1"))));
//                s.setM2(sb.getMassList().get(Integer.parseInt(atts.getValue("mass2"))));
//            } else {
//                s = new Spring(Spring.Type.Engine);
//
//                String m1Type = atts.getValue("m1Type");
//                if       ("Spring".equals(m1Type))   m1 = c.getSpringBodyList().get(Integer.parseInt(atts.getValue("body1"))).getMassList().get(Integer.parseInt(atts.getValue("mass1")));
//                else if  ("Rigid".equals(m1Type))    m1 = c.getRigidBodyList().get(Integer.parseInt(atts.getValue("body1"))).extraDynMassList.get(Integer.parseInt(atts.getValue("mass1")));
//                else
//                    m1 = c.getSpringBodyList().get(Integer.parseInt(atts.getValue("body1"))).getMassList().get(Integer.parseInt(atts.getValue("mass1")));
//                s.setM1(m1);
//
//                String m2Type = atts.getValue("m2Type");
//                if       ("Spring".equals(m2Type))   m2 = c.getSpringBodyList().get(Integer.parseInt(atts.getValue("body2"))).getMassList().get(Integer.parseInt(atts.getValue("mass2")));
//                else if  ("Rigid".equals(m2Type))    m2 = c.getRigidBodyList().get(Integer.parseInt(atts.getValue("body2"))).extraDynMassList.get(Integer.parseInt(atts.getValue("mass2")));
//                else
//                    m2 = c.getSpringBodyList().get(Integer.parseInt(atts.getValue("body2"))).getMassList().get(Integer.parseInt(atts.getValue("mass2")));
//                s.setM2(m2);
//            }
//        }

        s.setName(atts.getValue("name"));
        s.setKs(Double.parseDouble(atts.getValue("ks")));
        s.setKd(Double.parseDouble(atts.getValue("kd")));
        s.setKt(Double.parseDouble(atts.getValue("kt")));
        s.setStartLength(Double.parseDouble(atts.getValue("startlength")));
        s.setTearable(Boolean.parseBoolean(atts.getValue("tearable")));
        s.setAlive(Boolean.parseBoolean(atts.getValue("alive")));
        return s;
    }

    public static RSpring buildRSpring(Attributes atts, JETContext e, SpringBody sb) {
       RSpring s;
       
       String type = atts.getValue("type");
       if (sb!=null) {  // dealing with spring body spring
           s = new RSpring(null, null, null, RSpring.Type.SpringBody);
           s.setM1(sb.getMassList().get(Integer.parseInt(atts.getValue("mass1"))));
           s.setM2(sb.getMassList().get(Integer.parseInt(atts.getValue("mass2"))));
           s.setM3(sb.getMassList().get(Integer.parseInt(atts.getValue("mass3"))));
       } else {         // dealing with engine spring
           s = new RSpring(null, null, null, RSpring.Type.Engine);
           s.setM1(e.getSpringBodyList().get(Integer.parseInt(atts.getValue("body1"))).getMassList().get(Integer.parseInt(atts.getValue("mass1"))));
           s.setM2(e.getSpringBodyList().get(Integer.parseInt(atts.getValue("body2"))).getMassList().get(Integer.parseInt(atts.getValue("mass2"))));
           s.setM3(e.getSpringBodyList().get(Integer.parseInt(atts.getValue("body3"))).getMassList().get(Integer.parseInt(atts.getValue("mass3"))));
       }
       s.setName(atts.getValue("name"));
       s.setKs(Double.parseDouble(atts.getValue("ks")));
       s.setKd(Double.parseDouble(atts.getValue("kd")));
       s.setStartAngle(Double.parseDouble(atts.getValue("startangle")));
       s.setAlive(Boolean.parseBoolean(atts.getValue("alive")));
       return s;
    }

    public static Spring buildSMSpring(Attributes atts, SpringBody sb) {
        Spring s = new Spring(Spring.Type.ShapeMatch);
        s.setName(atts.getValue("name"));
        s.setKs(Double.parseDouble(atts.getValue("ks")));
        s.setKd(Double.parseDouble(atts.getValue("kd")));
        s.setStartLength(Double.parseDouble(atts.getValue("startlength")));
        s.setM1(sb.getMassList().get(Integer.parseInt(atts.getValue("sbmass"))));
        s.setM2(sb.rb.dynVecList.get(Integer.parseInt(atts.getValue("rbmass"))));
        s.setAlive(true);
        s.setTearable(false);
        return s;
    }

    public static Wall buildWall(Attributes atts, SpringBody sb) {
        Mass m1 = sb.getMassList().get(Integer.parseInt(atts.getValue("mass1")));
        Mass m2 = sb.getMassList().get(Integer.parseInt(atts.getValue("mass2")));
        Wall w = new Wall(m1, m2);
        return w;
    }

    public static Wall buildWall(Attributes atts, RigidBody rb) {
        Mass m1 = rb.dynVecList.get(Integer.parseInt(atts.getValue("mass1")));
        Mass m2 = rb.dynVecList.get(Integer.parseInt(atts.getValue("mass2")));
        Wall w = new Wall(m1, m2);
        return w;
    }

    public static SpringBody buildSpringBody(Attributes atts) {
        SpringBody sb = new SpringBody();
        sb.setName(atts.getValue("name"));
        sb.setMaterial(Integer.parseInt(atts.getValue("material")));
        sb.setMatchShape(Boolean.parseBoolean(atts.getValue("matchshape")));
        if (atts.getValue("smmi")!=null)
            sb.smMassIndices = splitToInts(atts.getValue("smmi"), ",");
        return sb;
    }

    public static void buildRigidBodyVertex(Attributes atts, Vec2d v, Mass m) {
        v.setX(Double.parseDouble(atts.getValue("baseX")));
        v.setY(Double.parseDouble(atts.getValue("baseY")));
        m.getPosition().setX(Double.parseDouble(atts.getValue("dynX")));
        m.getPosition().setY(Double.parseDouble(atts.getValue("dynY")));
        m.setMass(Double.parseDouble(atts.getValue("mass")));
    }

    public static RigidBody buildRigidBody(Attributes atts) {
        RigidBody rb = new RigidBody();
        rb.setName(atts.getValue("name"));
        rb.setMaterial(Integer.parseInt(atts.getValue("material")));
        rb.angle = Double.parseDouble(atts.getValue("angle"));
        rb.mass = Double.parseDouble(atts.getValue("mass"));
        rb.mI = Double.parseDouble(atts.getValue("mi"));
        rb.omega = Double.parseDouble(atts.getValue("omega"));
        rb.torque = Double.parseDouble(atts.getValue("torque"));
        rb.position.x = Double.parseDouble(atts.getValue("posX"));
        rb.position.y = Double.parseDouble(atts.getValue("posY"));
        rb.velocity.x = Double.parseDouble(atts.getValue("velX"));
        rb.velocity.y = Double.parseDouble(atts.getValue("velY"));
        rb.force.x = Double.parseDouble(atts.getValue("forceX"));
        rb.force.y = Double.parseDouble(atts.getValue("forceY"));
        return rb;
    }

    public static MyScript buildScript(Attributes atts) {
        MyScript s = new MyScript(atts.getValue("name"));
        return s;
    }

    public static void buildMaterialPair(Attributes atts, Scene s) {
        double f = Double.parseDouble(atts.getValue("friction"));
        double e = Double.parseDouble(atts.getValue("elasticity"));
        boolean collide = Boolean.parseBoolean(atts.getValue("collide"));
        int r = Integer.parseInt(atts.getValue("r"));
        int c = Integer.parseInt(atts.getValue("c"));
        s.getJETContext().materials[r][c].friction = f;
        s.getJETContext().materials[r][c].elasticity = e;
        s.getJETContext().materials[r][c].collide = collide;
    }

    public static Torque buildTorque(Attributes atts, Scene s) {
        String ctype = atts.getValue("center-type");
        if (!ctype.equals("mass")) throw new RuntimeException("Torques do not implement other center types than mass.");
        SpringBody sb = s.getJETContext().getSpringBodyList().get(Integer.parseInt(atts.getValue("center-body")));
        Mass m =    sb.getMassList().get(Integer.parseInt(atts.getValue("center-mass")));
        Torque t = new Torque(m, Double.parseDouble(atts.getValue("strength")));
        t.setName(atts.getValue("name"));
        return t;
    }

    public static ArrayList<Mass> buildMasslist(Attributes atts, Scene s) {
        ArrayList<Mass> list = new ArrayList<Mass>();
        SpringBody sb = s.getJETContext().getSpringBodyList().get(Integer.parseInt(atts.getValue("body")));
        for(int i : splitToInts(atts.getValue("masses"), ","))
            list.add(sb.getMassList().get(i));
        return list;
    }

    public static void buildSimulation(Attributes atts, Scene s) {
        s.getJETContext().setApplyGravity(Boolean.parseBoolean(atts.getValue("apply-gravity")));
        s.getJETContext().gravity = Double.parseDouble(atts.getValue("gravity"));
        s.getJETContext().setApplyDamping(Boolean.parseBoolean(atts.getValue("apply-damping")));
        s.getJETContext().damping = Double.parseDouble(atts.getValue("damping"));
        s.getJETContext().setDTime(Double.parseDouble(atts.getValue("timestep")));
        try {
            s.getJETContext().setMainBound(Double.parseDouble(atts.getValue("width")), Double.parseDouble(atts.getValue("height")));
        } catch(Exception e) {
            s.getJETContext().setMainBound(SCENE_START_WIDTH, SCENE_START_HEIGHT);
        }
    }
    
    ////////////////////////////////////////////////////////////////////////
    //                          H E L P E R S                             //
    ////////////////////////////////////////////////////////////////////////

    /**
     * Takes string with integer values separated with regex and creates an
     * array of those values.
     * @param values String with integer values separated with regex.
     * @param regex Regex expression for standard String split method (like comma for ex.)
     * @return New array with readed values.
     */
    public static int[] splitToInts(String values, String regex) {
        String[] s = values.split(regex);
        int[] array = new int[s.length];
        for(int i=0; i<s.length; i++) {
            array[i] = Integer.parseInt(s[i].trim());
        }
        return array;
    }

    ////////////////////////////////////////////////////////////////////////////
    //                  S A X   P R O J E C T   L O A D E R                   //
    ////////////////////////////////////////////////////////////////////////////
    
    public static class JETProjectSAXHandler extends DefaultHandler implements Constants {

        private static final ControlCenter CC = ControlCenter.getInstance();
        private boolean debug = false;

        private enum RBLocation {
            BaseVertices,
            ExtraVertices
        }

        private Scene       curScene;       // currently parsed scene
        private SpringBody  curSB;          // currently parsed spring body
        
        private RigidBody   curRB;          // currently parsed rigid body
        private RBLocation  curRBLoc;       // current rigid body location
        
        private Mass        curMass;        // currently parsed mass
        private Spring      curSpring;      // currently parsed spring
        private RSpring     curRSpring;     // currently parsed rotary spring
        private Spring      curSMSpring;    // currently parsed shape matching spring
        private Wall        curWall;        // currently parsed wall
        private Vec2d       curBSV;         // currently parsed base shape vector
        private MyScript    curScript;      // currently parsed script
        private Torque      curTorque;      // currently parsed torque

        private String      scenePath;      // helper string where to add new scene
        private ArrayList<Spring> sbSMSprings = new ArrayList<Spring>();    // helper to maintain shape matching spring body springs while they are loaded...
        
        @Override
        public void startDocument() throws SAXException {
            if (debug) System.out.println("doc start");
        }
        @Override
        public void endDocument() throws SAXException {
            if (debug) System.out.println("doc end");
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
            if (debug) System.out.println("element start: "+qName);

            if (ELEMENT_NAME_SCENE.equals(qName)) {
                curScene = new Scene();
                curScene.setName(atts.getValue("name"));
                scenePath = atts.getValue("path");
            } else if (ELEMENT_NAME_SIMULATION.equals(qName)) {
                if (curScene!=null) buildSimulation(atts, curScene);
            } else if (ELEMENT_NAME_SPRINGBODY.equals(qName)) {
                curSB = buildSpringBody(atts);
            } else if (ELEMENT_NAME_RIGIDBODY.equals(qName)) {
                curRB = buildRigidBody(atts);
            } else if (ELEMENT_NAME_MASS.equals(qName)) {
                curMass = buildMass(atts);
            } else if (ELEMENT_NAME_POSITION.equals(qName)) {
                if (curMass != null) {
                    curMass.setPosition(buildVec2d(atts));
                }
            } else if (ELEMENT_NAME_VELOCITY.equals(qName)) {
                if (curMass!=null) {
                    curMass.setVelocity(buildVec2d(atts));
                }
            } else if (ELEMENT_NAME_FORCE.equals(qName)) {
                if (curMass!=null) {
                    curMass.setForce(buildVec2d(atts));
                }
            } else if (ELEMENT_NAME_SPRING.equals(qName)) {
                curSpring = buildSpring(atts, curScene.getJETContext(), curSB, curRB);
            } else if (ELEMENT_NAME_RSPRING.equals(qName)) {
                curRSpring = buildRSpring(atts, curScene.getJETContext(), curSB);
            } else if (ELEMENT_NAME_WALL.equals(qName)) {
                if (curSB!=null) {
                    curWall = buildWall(atts, curSB);
                } else if (curRB!=null) {
                    curWall = buildWall(atts, curRB);
                }
            } else if (ELEMENT_NAME_SMSPRING.equals(qName)) {
                curSMSpring = buildSMSpring(atts, curSB);
            } else if (ELEMENT_NAME_SMSPRINGS.equals(qName)) {
                if (curSB!=null) {
                    curSB.smSprings = new Spring[Integer.parseInt(atts.getValue("count"))];
                    sbSMSprings.clear();
                }
            } else if (ELEMENT_NAME_BASESHAPEVECTOR.equals(qName)) {
                curBSV = buildVec2d(atts);
            } else if (ELEMENT_NAME_SCRIPT.equals(qName)) {
                curScript = buildScript(atts);
            } else if (ELEMENT_NAME_MATERIALPAIR.equals(qName)) {
                if (curScene!=null) {
                    buildMaterialPair(atts, curScene);
                }
            } else if (ELEMENT_NAME_TORQUE.equals(qName)) {
                if (curScene!=null) {
                    curTorque = buildTorque(atts, curScene);
                }
            } else if (ELEMENT_NAME_MASSLIST.equals(qName)) {
                if (curTorque!=null) {
                    curTorque.getMassList().addAll(buildMasslist(atts, curScene));
                }
            } else if ("rbvertices".equals(qName)) {
                curRBLoc = RBLocation.BaseVertices;
            } else if ("rbextravertices".equals(qName)) {
                curRBLoc = RBLocation.ExtraVertices;
            } else if ("rbvertex".equals(qName)) {
                if (curRB!=null) {
                    Mass m = new Mass(1,1,1);
                    Vec2d v = new Vec2d();
                    buildRigidBodyVertex(atts, v, m);
                    switch(curRBLoc) {
                        case BaseVertices:
                            curRB.baseVecList.add(v);
                            curRB.dynVecList.add(m);
                            break;
                        case ExtraVertices:
                            curRB.extraBaseVecList.add(v);
                            curRB.extraDynMassList.add(m);
                            break;
                    }
                }
            }
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if (debug) System.out.println("element end: "+qName);
            
            if (ELEMENT_NAME_SCENE.equals(qName)) {
                CC.addScene(curScene, curScene.getName(), scenePath);
                DataModelsFactory.getProjectTreeModel().fireWholeTreeChanged();
                curScene = null;
            } else if (ELEMENT_NAME_SPRINGBODY.equals(qName)) {
                if (curScene!=null) curScene.getJETContext().addSpringBody(curSB);
                curSB = null;
            } else if (ELEMENT_NAME_RIGIDBODY.equals(qName)) {
                if (curSB!=null) curSB.rb = curRB;
                else if (curScene!=null) curScene.getJETContext().addRigidBody(curRB);
                curRB = null;
            } else if (ELEMENT_NAME_MASS.equals(qName)) {
                if (curSB!=null) {
//                    if (curRB!=null) {// adding mass to rigid body of spring body
//                        curRB.dynVecList.add(curMass);
//                    } else {
                       curSB.addMass(curMass); // adding mass to spring body of a scene
//                    }
                } else if (curRB!=null) {}
                curMass = null;
            } else if (ELEMENT_NAME_SPRING.equals(qName)) {
                if (curSB!=null)    curSB.addSpring(curSpring);
                else                curScene.getJETContext().addSpring(curSpring);
                curSpring = null;
            } else if (ELEMENT_NAME_RSPRING.equals(qName)) {
                if (curSB!=null)    curSB.addRSpring(curRSpring);
                else                curScene.getJETContext().addRSpring(curRSpring);
                curRSpring = null;
            } else if (ELEMENT_NAME_WALL.equals(qName)) {
                if (curSB!=null) curSB.addWallToBorder(curWall);
                else if (curRB!=null) curRB.border.addWall(curWall);
                curWall = null;
            } else if (ELEMENT_NAME_SMSPRING.equals(qName)) {
                if (curSB!=null) {
                    sbSMSprings.add(curSMSpring);
                }
            } else if (ELEMENT_NAME_SMSPRINGS.equals(qName)) {
                if (curSB!=null) {
                    for(int i=0; i<sbSMSprings.size(); i++)
                        curSB.smSprings[i] = sbSMSprings.get(i);
                }
            } else if (ELEMENT_NAME_BASESHAPEVECTOR.equals(qName)) {
                if (curRB!=null) curRB.baseVecList.add(curBSV);
                curBSV = null;
            } else if (ELEMENT_NAME_SCRIPT.equals(qName)) {
                if (curScene!=null) curScene.SC.addScript(curScript);
                curScript = null;
            } else if (ELEMENT_NAME_MATERIALPAIR.equals(qName)) {
                // Do nothing
            } else if (ELEMENT_NAME_TORQUE.equals(qName)) {
                if (curScene!=null) curScene.getJETContext().addTorque(curTorque);
                curTorque = null;
            } else if (ELEMENT_NAME_MASSLIST.equals(qName)) {
                // Do nothing
            }
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            if (debug) System.out.println("chars: "+curScript.getScript());
            if (curScript!=null) {
                String text = curScript.getScript()!=null ? curScript.getScript() : "";
                text += new String(ch, start, length);
                curScript.setScript(text);
                
            }
        }
    }

    public static Document createNewDocument() throws ParserConfigurationException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        return db.newDocument();
    }

    public static void loadProject() {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        try {
            SAXParser saxParser = factory.newSAXParser();
            saxParser.parse(new File("XMLHelper2_test.xml"), new JETProjectSAXHandler());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static void loadProject(String path) {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        try {
            SAXParser saxParser = factory.newSAXParser();
            saxParser.parse(new File(path), new JETProjectSAXHandler());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //                          T E S T I N G                                 //
    ////////////////////////////////////////////////////////////////////////////

    public static void main(String[] args) throws ParserConfigurationException, TransformerConfigurationException, FileNotFoundException, TransformerException, IOException, SAXException {
        Mass m = new Mass(100, 200, 1.5);

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.newDocument();

        Element e = buildMassElement(doc, m);

        System.out.println("m: "+m);
        System.out.println("e: "+e);

        doc.appendChild(e);

        // zapisywanie
        /*
        Transformer t = TransformerFactory.newInstance().newTransformer();

        FileOutputStream foStream = new FileOutputStream("XMLHelper2_test.xml");
        t.transform(new DOMSource(doc), new StreamResult(foStream));
        foStream.close();
        */

        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser saxParser = factory.newSAXParser();

        saxParser.parse(new File("XMLHelper2_test.xml"), new JETProjectSAXHandler());
    }
}